﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Speech.AudioFormat;
using Microsoft.Speech.Recognition;
using System.IO;
using Microsoft.Kinect;


namespace Kinect_2DPainter
{
    /// <summary>
    /// Visual System
    /// </summary>
    public class BuildImage
    {
        private WriteableBitmap colorBitmap;
        private byte[] colorPixels;
        private MainWindow MWW;
        private Rectangle[] rect_array = new Rectangle[5];
        private static int K = 10;
        private static byte[] white_array = new byte[K * K * 4];

        public void LoadImage(MainWindow MW)
        {
            //Enable kinect IR stream. :)
            MW.KS.EnableIRStream();
            //Enable Kinect SkeletonStream
            MW.KS.EnableSkeletonStream();
            // create color
            ChangeBrushColor(RecordingStudio.COLOR.BLACK);

            MWW = MW;

            this.colorPixels = new byte[MW.KS.sensor.ColorStream.FramePixelDataLength];
            this.colorBitmap = new WriteableBitmap(MW.KS.sensor.ColorStream.FrameWidth, MW.KS.sensor.ColorStream.FrameHeight, 96.0, 96.0, PixelFormats.Bgra32, null);

            MW.Image.Source = this.colorBitmap;
            //Add even handler in this class.
            //MW.KS.sensor.ColorFrameReady += this.UpdateColorBitmap;

            MW.KS.sensor.SkeletonFrameReady += this.UpdateSkeletonCanvas;
            // call once for creating skeleton rectangle.

        }

        private Point skeletionPointToScreen(SkeletonPoint skelpoint)
        {
            DepthImagePoint depthPoint = MWW.KS.sensor.CoordinateMapper.MapSkeletonPointToDepthPoint(skelpoint, DepthImageFormat.Resolution640x480Fps30);
            return new Point(depthPoint.X, depthPoint.Y);
        }

        public void UpdateColorBitmap(Object sender, ColorImageFrameReadyEventArgs e)
        {
            using (ColorImageFrame colorFrame = e.OpenColorImageFrame())
            {
                if (colorFrame != null)
                {
                    // copy the pixel data from the image to a temporary array
                    colorFrame.CopyPixelDataTo(this.colorPixels);

                    // Write the pixel data into our bitmap
                    this.colorBitmap.WritePixels(
                        new Int32Rect(0, 0, this.colorBitmap.PixelWidth, this.colorBitmap.PixelHeight),
                        this.colorPixels,
                        this.colorBitmap.PixelWidth * colorFrame.BytesPerPixel,
                        0);
                }
            }
        }

        public void UpdateSkeletonCanvas(Object sender, SkeletonFrameReadyEventArgs e)
        {
            Skeleton[] skeletons = new Skeleton[0];
            using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())
            {
                if (skeletonFrame != null)
                {
                    skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength];
                    skeletonFrame.CopySkeletonDataTo(skeletons);
                }
            }
            // The code actually allows many people to draw. 
            // Add better stuff to make only default skeleton to draw.
            foreach (Skeleton skel in skeletons)
            {
                if (skel.TrackingState == SkeletonTrackingState.Tracked)
                {
                    //if RecordingState is Enabled.
                    if (JointTrackingState.Tracked == skel.Joints[JointType.Head].TrackingState)
                    {
                        if (RecordingStudio.COLOR_CHANGE)
                        {
                            ChangeBrushColor(RecordingStudio.BRUSH_COLOR);
                            RecordingStudio.COLOR_CHANGE = false;
                        }
                        if (RecordingStudio.DRAWING_STATE)
                        {
                            Joint joint = skel.Joints[JointType.HandRight];
                            Point p = skeletionPointToScreen(joint.Position);
                            int X = clipPoint(p.X, 630);
                            int Y = clipPoint(p.Y, 470);
                            if (JointTrackingState.Tracked == joint.TrackingState )
                            { // draw white box.
                                this.colorBitmap.WritePixels(new Int32Rect(X, Y, K, K), white_array, K * 4, 0);
                            }
                        }
                    }
                }
            }

        }

        public void ChangeBrushColor(RecordingStudio.COLOR color)
        {
            byte B,R,G,A;
            switch (color)
            {
                case RecordingStudio.COLOR.RED:
                    B = 0;R=255; G= 0; A = 255;
                    break;
                case RecordingStudio.COLOR.GREEN:
                    B=0;R = 0; G=255;A = 255;
                    break;
                case RecordingStudio.COLOR.BLUE:
                    B=255;R = 0 ; G = 0; A = 255;
                    break;
                default:
                    B= 0; R = 0; G = 0; A = 255;
                    break;
            }
            for (int i = 0; i < white_array.Length/4 ; i++)
            {
                white_array[4*i] =B;
                white_array[4*i+1] = G;
                white_array[4*i+2] = R;
                white_array[4*i+3] = A;
            }

        }

        private int clipPoint(double p, int Com)
        {
            int X = Convert.ToInt32(p);
            if (X < 1)
                X = 1;
            else if (X > Com - 4)
                X = Com - 4;
            return X;
        }

    }

    ///<summary>
    /// Actual System
    /// </summary>
    public static class RecordingStudio
    {
        public enum COLOR { RED,GREEN,BLUE,WHITE,BLACK};
        //members
        public static bool DRAWING_STATE = false;
        public static COLOR BRUSH_COLOR = COLOR.RED;
        public static bool CANVAS_RESET = false;
        public static bool COLOR_CHANGE = false;

        private static MainWindow MWW;

        public static void RecordingStudioConstructor(MainWindow MW)
        {
            MWW = MW;
        }

    }

    /// <summary>
    /// Kinect Sensor
    /// </summary>
    public class KinectClass
    {
        //members

        /// <summary>
        ///  Active Kienct Sensor
        /// </summary>
        public KinectSensor sensor;

        public KinectClass()
        {
            foreach (var potentialSensor in KinectSensor.KinectSensors)
            {
                if (KinectStatus.Connected == potentialSensor.Status)
                {
                    sensor = potentialSensor;
                    break;
                }
            }

            if (null != sensor)
            {
                try
                {
                    sensor.Start();
                }
                catch (IOException)
                {
                    sensor = null;
                }
            }

        }

        public void EnableIRStream()
        {
            sensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);
        }

        public void EnableSkeletonStream()
        {
            //sensor.DepthStream.Enable(DepthImageFormat.Resolution640x480Fps30);
            sensor.SkeletonStream.Enable();
        }

    }

    /// <summary>
    /// Voice Command Class
    /// </summary>
    public class VoiceCommands
    {
        // Properties
        private SpeechRecognitionEngine speechEngine;
        private MainWindow window_label;
        // Command Language


        // Methods

        /// <summary>
        /// Constructor
        /// </summary>
        public VoiceCommands(MainWindow window_label)
        {
            this.window_label = window_label;
        }

        /// <summary>
        /// Gets the metadata for the speech recognizer (acoustic model) most suitable to 
        /// process audio from kinect device.
        /// </summary>
        /// <returns>
        /// RecognizerInfo if found, <code>null</code> otherwise.
        /// </returns>
        private static RecognizerInfo GetKinectRecognizer()
        {
            foreach (RecognizerInfo recognizer in SpeechRecognitionEngine.InstalledRecognizers())
            {
                string value;
                recognizer.AdditionalInfo.TryGetValue("Kinect", out value);
                if ("True".Equals(value, StringComparison.OrdinalIgnoreCase) && "en-US".Equals(recognizer.Culture.Name, StringComparison.OrdinalIgnoreCase))
                {
                    return recognizer;
                }
            }
            return null;
        }

        public void SetUpSpeechEngine()
        {
            RecognizerInfo ri = GetKinectRecognizer();
            if (null != ri)
            {

                this.speechEngine = new SpeechRecognitionEngine(ri.Id);
                // Add code to programatically add text
                // I used grammer
                // C:\Users\ashokjal\Documents\Visual Studio 2010\TeapotSaver\TeapotSaver\SpeechCodes.xml
                //Create a grammer from grammer definition XML file.

                /*   using (var memoryStream = new MemoryStream(Encoding.ASCII.GetBytes("SpeechCodes.xml")))
                {
                    var g = new Grammar(memoryStream);
                    speechEngine.LoadGrammar(g);
                }  */

                var directions = new Choices();
                directions.Add(new SemanticResultValue("draw", "START_DRAWING"));
                directions.Add(new SemanticResultValue("stop", "STOP_DRAWING"));
                directions.Add(new SemanticResultValue("red", "RED"));
                directions.Add(new SemanticResultValue("green", "GREEN"));
                directions.Add(new SemanticResultValue("blue", "BLUE"));
                directions.Add(new SemanticResultValue("save picture", "SAVE_PICTURE"));
                directions.Add(new SemanticResultValue("reset", "RESET"));

                var gb = new GrammarBuilder { Culture = ri.Culture };
                gb.Append(directions);
                var g = new Grammar(gb);
                speechEngine.LoadGrammar(g);

                speechEngine.SpeechRecognized += SpeechRecognized;
                speechEngine.SpeechRecognitionRejected += SpeechRejected;

                //Add Code for Kinect Handler
                speechEngine.SetInputToAudioStream(window_label.KS.sensor.AudioSource.Start(), new SpeechAudioFormatInfo(EncodingFormat.Pcm, 16000, 16, 1, 32000, 2, null));
                speechEngine.RecognizeAsync(RecognizeMode.Multiple);
            }
            else
            {
                // Speech Recognizer has failed.
            }
        }

        /// <summary>
        /// Handler for recognized speech events.
        /// </summary>
        /// <param name="sender">object sending the event.</param>
        /// <param name="e">event arguments.</param>
        private void SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            const double ConfidenceThreshold = 0.3;
            if (ConfidenceThreshold < e.Result.Confidence)
            {
                window_label.log_label.Content += "\n" + e.Result.Semantics.Value.ToString();
                switch (e.Result.Semantics.Value.ToString())
                {
                    case "START_DRAWING":
                        RecordingStudio.COLOR_CHANGE = true;
                        RecordingStudio.DRAWING_STATE = true;
                        break;
                    case "STOP_DRAWING":
                        RecordingStudio.COLOR_CHANGE = true;
                        RecordingStudio.DRAWING_STATE = false;
                        break;
                    case "RED":
                        RecordingStudio.COLOR_CHANGE = true;
                        RecordingStudio.BRUSH_COLOR = RecordingStudio.COLOR.RED;
                        break;
                    case "GREEN":
                        RecordingStudio.COLOR_CHANGE = true;
                        RecordingStudio.BRUSH_COLOR = RecordingStudio.COLOR.GREEN;
                        break;
                    case "BLUE":
                        RecordingStudio.COLOR_CHANGE = true;
                        RecordingStudio.BRUSH_COLOR = RecordingStudio.COLOR.BLUE;
                        break;
                    case "RESET":
                        RecordingStudio.COLOR_CHANGE = true;
                        RecordingStudio.CANVAS_RESET = true;
                        break;
                }
            }
        }

        /// <summary>
        /// Handler for rejected speech events.
        /// </summary>
        /// <param name="sender">object sending the event.</param>
        /// <param name="e">event arguments.</param>
        private void SpeechRejected(object sender, SpeechRecognitionRejectedEventArgs e)
        {
            window_label.log_label.Content = "words Unrecognized";
        }
    }

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private VoiceCommands VC;
        public KinectClass KS;
        private BuildImage BI;
        //private RecordingStudio rs;

        public MainWindow()
        {
            InitializeComponent();
            // Initiate Kinect 
            KS = new KinectClass();

            RecordingStudio.RecordingStudioConstructor(this);

            // Load IR Image
            BI = new BuildImage();
            BI.LoadImage(this);

            // Intiate Voice Commands
            VC = new VoiceCommands(this);
            VC.SetUpSpeechEngine();

            /*
              //Testing Canvas
              rect = new Rectangle( );
              rect.Width = 10;
              rect.Height = 10;
              rect.Fill = new SolidColorBrush(Colors.Red);
              // Set canvas position
              Canvas.SetLeft(rect,10);
              Canvas.SetTop(rect,10);
              this.canvas1.Children.Add(rect);
            */

        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            log_label.Content = "LOgging works. \n Now with Audio";
        }
    }

}
